#include "Components/ComponentAttacker.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentPlayer.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentClothing.h"
#include "Components/ComponentLevel.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentMiner.h"
#include "Components/ComponentDamage.h"
#include "Components/ComponentCreatureModel.h"

#include "Player/PlayerData.h"
#include "Log.h"
#include "Engine/FastColor4.hpp"
#include "Network/PackageManager.h"
#include "Network/Packages/ComponentHealthPackage.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemBodies.h"
#include "Subsystems/SubsystemTerrain.h"


#include "Game/BodyRaycastResult.hpp"
#include "Game/MovingBlocksRaycastResult.hpp"
#include "GameRandom.hpp"
#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "Expand/EventManager.h"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    /**
     * @brief 攻击处理
     * @param target 被攻击的目标
     * @param attacker 攻击者
     * @param hitPosition 攻击的位置
     * @param hitDirection 攻击的方向
     * @param attackPower 攻击的攻击力
     * @param isMeleeAttack 是否是近战攻击
    */
    void ComponentAttacker::AttackBody(ComponentBody *target, entt::entity attackerEntity, const Vector3 &hitPosition, const Vector3 &hitDirection, float attackPower, bool isMeleeAttack)
    {
        if (attackPower > 0.0f)
        {
            ComponentPlayer *componentPlayer = target->componentPlayer;
            if (componentPlayer != nullptr)
            {
                // 衣服抗伤害后的剩余攻击力
                attackPower = componentPlayer->componentClothing->applyArmorProtection(attackPower);
                // 自身防御处理
                attackPower /= componentPlayer->componentLevel->getResilienceFactor();
            }

            // 被攻击的目标必须有 ComponentHealth 或 ComponentDamage 组件才有伤害处理
            ComponentHealth **componentHealthPtr = GameSingleton::gameRegistry->try_get<ComponentHealth *>(target->entity);
            if (componentHealthPtr != nullptr)
            {
                ComponentHealth *componentHealth = *componentHealthPtr;
                float num = attackPower / componentHealth->attackResilience;
                if (num > 0.0f)
                {
                    std::string cause = "受到不明的伤害";
                    if (attackerEntity != entt::null)
                    {
                        ComponentPlayer **componentPlayerPtr = GameSingleton::gameRegistry->try_get<ComponentPlayer *>(attackerEntity);
                        if (componentPlayerPtr != nullptr)
                        {
                            cause = fmt::format("被 {} 噶了", (*componentPlayerPtr)->playerData->username);
                        }
                        else
                        {
                            ComponentCreature **componentCreaturePtr = GameSingleton::gameRegistry->try_get<ComponentCreature *>(attackerEntity);
                            if (componentCreaturePtr != nullptr)
                            {
                                cause = fmt::format("被 {} 干掉了", (*componentCreaturePtr)->displayName);
                            }
                            else
                            {
                                Log::Warn("找不到 ComponentCreature 的实体触发了一次攻击");
                            }
                        }
                    }
                    float health = componentHealth->health;
                    componentHealth->injure(num, attackerEntity, false, cause, InjureType::BeAttacked);
                    float num2 = (health - componentHealth->health) * componentHealth->attackResilience;

                    // if(num2 > 0.0f)
                    {
                        // 告诉客户端哪里需要伤害的文字提示
                        // 如果是致命伤，那么提示的文字颜色就用红色
                        Color4 color = (componentHealth->health == 0.0f ? FastColor4::Red : FastColor4::White);
                        std::string text = std::to_string((int32_t)(-num2 - 0.5f));
                        Vector3 position = hitPosition + 0.75 * hitDirection;
                        GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentHealthPackage>(position, hitDirection, color, text));
                    }
                }
            }

            // ComponentDamage 组件一般是船的专属组件
            ComponentDamage **componentDamagePtr = GameSingleton::gameRegistry->try_get<ComponentDamage *>(target->entity);
            if (componentDamagePtr != nullptr)
            {
                ComponentDamage *componentDamage = *componentDamagePtr;
                float num3 = attackPower / componentDamage->attackResilience;
                componentDamage->damage(num3);
            }

            ComponentBody **attackerBodyPtr = GameSingleton::gameRegistry->try_get<ComponentBody *>(attackerEntity);
            float num4 = 0.0f;
            float x = 0.0f;
            if (isMeleeAttack && attackerEntity != entt::null && attackerBodyPtr != nullptr)
            {
                float num5 = (attackPower >= 2.0f) ? 1.25f : 1.0f;
                float num6 = MathUtils::Pow((*attackerBodyPtr)->mass / target->mass, 0.5f);
                float x2 = num5 * num6;
                num4 = 5.5f * MathUtils::Saturate(x2);
                x = 0.25f * MathUtils::Saturate(x2);
            }
            else if (attackPower > 0.0f)
            {
                num4 = 2.0f;
                x = 0.2f;
            }
            // 被攻击者的击退效果
            if (num4 > 0.0f)
            {
                Vector3 impulse = num4 * Vector3::Normalize(hitDirection * 0.9 + 0.05f * FastVector3::UnitY);
                target->applyImpulse(impulse);
            }
        }
    }

    /**
     * @brief 打击对应的实体
     * @param entity 打击的实体
     * @param hitPosition 打击的位置
     * @param hitDirection 打击的方向
     * @note 被打击得实体必须有 ComponentBody 组件才有效
    */
    void ComponentAttacker::hit(entt::entity targetEntity, const Vector3 &hitPosition, const Vector3 &hitDirection)
    {
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();

        if (timeOfDay->getGameTime() - lastHitTime < 0.66)
        {
            return;
        }
        if (GameSingleton::gameRegistry->valid(targetEntity) == true)
        {
            ComponentBody **componentBodyPtr = GameSingleton::gameRegistry->try_get<ComponentBody *>(targetEntity);
            if (componentBodyPtr != nullptr)
            {
                hitBody(*componentBodyPtr, hitPosition, hitDirection);
                // Log::info(fmt::format("hitPosition: {},{},{}   hitDirection: {},{},{}", hitPosition.posX, hitPosition.posY, hitPosition.posZ, hitDirection.posX, hitDirection.posY, hitDirection.posZ));

            }
        }
    }

    void ComponentAttacker::hitBody(ComponentBody *targetBody, const Vector3 &hitPosition, const Vector3 &hitDirection)
    {
        Vector3 position;
        componentBody->getPosition(position);

        Vector3 hitLenVector3 = hitPosition - position;
        float hitLength = hitLenVector3.length();
        if (hitLength > 5.0f)
        {
            ComponentPlayer *targetPlayer = targetBody->componentPlayer;
            if (componentMiner != nullptr)
            {
                std::string playerName = componentMiner->componentPlayer->playerData->username;
                if (targetPlayer != nullptr)
                {
                    Log::Warn(fmt::format("{} 攻击 {} 的目标距离超过5，距离为{}", playerName, targetPlayer->playerData->username, hitLength));
                }
                else
                {
                    Log::Warn(fmt::format("{} 攻击 {} 的目标距离超过5，距离为{}", playerName, (uint16_t)targetBody->entity, hitLength));
                }
            }
            else
            {
                Log::Warn(fmt::format("{} 攻击 {} 的目标距离超过5，距离为{}", (uint16_t)entity, (uint16_t)targetBody->entity, hitLength));
            }
            return;
        }
        Vector3 hitBodyLenth = position - hitPosition;
        hitLength = hitBodyLenth.length();
        if (hitLength > 6.5f)
        {
            Log::Warn(fmt::format("{} 所处的位置是:{},{},{} 却发动了打击位置:{},{},{}", (uint16_t)entity, position.posX, position.posY, position.posZ, hitPosition.posX, hitPosition.posY, hitPosition.posZ));
            return;
        }

        // 生物攻击捕捉
        if (GameSingleton::eventManager->creatureEvent->hitBody(this, targetBody, hitPosition, hitDirection) == false)
        {
            return;
        }

        lastHitTime = timeOfDay->getGameTime();

        float injureValue = 0.0f;
        float hitRate = 1.0f;
        if (componentMiner != nullptr)
        {
            int32_t blockValue = componentMiner->getActiveBlockValue();
            Block *block = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(blockValue)];
            if (blockValue != 0)
            {
                injureValue = block->getMeleePower(blockValue) * attackPower * GameSingleton::gameRandom->getFloat(0.8f, 1.2f);
                hitRate = block->getMeleeHitProbability(blockValue);
            }
            else
            {
                injureValue = attackPower * GameSingleton::gameRandom->getFloat(0.8f, 1.2f);
                hitRate = 0.66f;
            }
            injureValue *= componentMiner->componentPlayer->componentLevel->getStrengthFactor();
        }
        else
        {
            injureValue = attackPower * GameSingleton::gameRandom->getFloat(0.8f, 1.2f);
            hitRate = 0.66f;
        }

        if (GameSingleton::gameRandom->getBool(hitRate))
        {
            AttackBody(targetBody, this->entity, hitPosition, hitDirection, injureValue, true);
            if (componentMiner != nullptr)
            {
                componentMiner->damageActiveTool(1);
            }
        }
        else
        {
            Vector3 position = hitPosition + 0.75f * hitDirection;
            Vector3 velocity = 1.0f * hitDirection;
            GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentHealthPackage>(position, hitDirection, FastColor4::White, "Miss"));
        }
    }

    RaycastData ComponentAttacker::raycast(const Ray3 &ray, RaycastMode mode)
    {
        RaycastData raycastData = {nullptr};
        float reach = 5.0f;
        Vector3 creaturePosition = componentBody->componentCreature->componentCreatureModel->getEyePosition();
        Vector3 start = ray.Position;
        Vector3 direction = Vector3::Normalize(ray.Direction);
        Vector3 end = ray.Position + direction * 15.0f;
        Point3 startCell = Terrain::ToCell(start);
        std::shared_ptr<BodyRaycastResult> bodyRaycastResult = GameSingleton::singletonPtr->getSingleton<SubsystemBodies>()->raycast(
            start,
            end,
            0.35f,
            [&](ComponentBody *body, float distance) -> bool
            {
                return (Vector3::DistanceSquared(start + distance * direction, creaturePosition) <= reach * reach &&
                        body->entity != this->entity &&
                        Vector3::Dot(Vector3::Normalize(body->getBoundingBox().center() - start), direction) > 0.7f);
            });
        std::shared_ptr<MovingBlocksRaycastResult> movingBlocksRaycastResult = nullptr;
        std::shared_ptr<TerrainRaycastResult> terrainRaycastResult = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>()->raycast(
            start,
            end,
            true,
            true,
            [&](int32_t value, float distance) -> bool
            {
                if (Vector3::DistanceSquared(start + distance * direction, creaturePosition) <= reach * reach)
                {
                    Block *block = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(value)];
                    if (distance == 0.0f && Vector3::Dot(direction, Vector3(startCell) + 0.5f - start) < 0.0f)
                    {
                        return false;
                    }
                    if (mode == RaycastMode::Digging)
                    {
                        return block->IsDiggingTransparent == false;
                    }
                    if (mode == RaycastMode::Interaction)
                    {
                        if (block->IsPlacementTransparent)
                        {
                            return block->isInteractive(value);
                        }
                        return true;
                    }
                    if (mode == RaycastMode::Gathering)
                    {
                        return block->IsGatherable;
                    }
                }
                return false;
            });
        float num = bodyRaycastResult != nullptr ? bodyRaycastResult->distance : FLT_MAX;
        float num2 = movingBlocksRaycastResult != nullptr ? movingBlocksRaycastResult->distance : FLT_MAX;
        float num3 = terrainRaycastResult != nullptr ? terrainRaycastResult->distance : FLT_MAX;
        if (num < num2 && num < num3)
        {
            raycastData.bodyRaycastResult = bodyRaycastResult;
            return raycastData;
        }
        if (num2 < num && num2 < num3)
        {
            raycastData.movingBlocksRaycastResult = movingBlocksRaycastResult;
            return raycastData;
        }
        if (num3 < num && num3 < num2)
        {
            raycastData.terrainRaycastResult = terrainRaycastResult;
            return raycastData;
        }
        raycastData.ray3 = std::make_shared<Ray3>(start, direction);
        return raycastData;
    }



    static const std::string componentName = "Attacker";
    const std::string &ComponentAttacker::getName() const
    {
        return componentName;
    }
    const std::string &ComponentAttacker::GetName()
    {
        return componentName;
    }
    void ComponentAttacker::load(const nlohmann::json &jsonData)
    {
        ComponentMiner **componentMinerPtr = GameSingleton::gameRegistry->try_get<ComponentMiner *>(entity);
        if (componentMinerPtr != nullptr)
        {
            componentMiner = *componentMinerPtr;
        }
        componentBody = GameSingleton::gameRegistry->get<ComponentBody *>(entity);

        timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();

        attackPower = jsonData["AttackPower"].get<float>();
    }

} // namespace PocketSurvival
